/**
 * 
 */
package merisis.util;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class WordArrayTrunk {

	private long trunkno;
	private File home;
	private byte scale;
	private long maskOfWordPosition;
	private int byteLengthOfTrunkHead = 24;

	public WordArrayTrunk(File home, byte scale) {

		this.scale = scale;
		this.maskOfWordPosition = -1 >>> 64 - scale;
		this.home = home;
	}

	private RandomAccessFile datafile;
	private FileChannel channel;

	public void open(long trunkno) throws IOException {

		this.trunkno = trunkno;

		File fname = new File(home, encodeBase32(trunkno, 8) + ".laa");
		fname.delete();

		datafile = new RandomAccessFile(fname, "rw");
		channel = datafile.getChannel();

		if (datafile.length() == 0) {
			ByteBuffer filehead = ByteBuffer.allocate(32);
			byte[] head = new byte[byteLengthOfTrunkHead];
			head[23] = (byte) 0xFF; // the end of head
			filehead.put(head);
			filehead.putLong(1);
			filehead.flip();
			channel.write(filehead);
			channel.force(true);
		}
	}

	public long read(long posOfWord) throws IOException {

		long bytepos = byteLengthOfTrunkHead
				+ ((posOfWord & maskOfWordPosition) << 3);
		
		ByteBuffer buf = ByteBuffer.allocate(8);
		channel.read(buf, bytepos);
		buf.flip();

		long word = buf.getLong();
		return word;
	}

	/**
	 * To append a word array.
	 * 
	 * @return the begin word postion.
	 */
	public long append(ByteBuffer wordbuf) throws IOException {

		wordbuf.flip();
		
		long bytepos = datafile.length();

		channel.position(bytepos).write(wordbuf);
		channel.force(false);

		return (trunkno << scale) + ((bytepos - byteLengthOfTrunkHead) >>> 3);
	}

	public void close() throws IOException {

		channel.close();
		datafile.close();
	}

	private static char[] codetable = { '0', '1', '2', '3', '4', '5', '6', '7', //
			'8', '9', 'A', 'B', 'C', 'D', 'E', 'F', //
			'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', //
			'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', // 
	};

	private static String encodeBase32(long no, int len) {

		StringBuilder sb = new StringBuilder();
		sb.setLength(len);

		int pos = len - 1;
		do {
			sb.setCharAt(pos--, codetable[(int) (no & 0x1F)]);
			no >>>= 5;
		} while (no > 0);

		while (pos >= 0)
			sb.setCharAt(pos--, '0');

		return sb.toString();
	}
}